
#include "ip2366.h"
#include "Logout.h"
#include "fast-math.h"

static const char *TAG = "ip2366";

void IP2366_Init(ip2366_state_t *ip)
{
    // 在 IP2366 INT 引脚持续为高 100ms 之后， MCU 可以进行 I2C 通信
    HAL_GPIO_WritePin(ip->IP_INT_GPIOx, ip->IP_INT_GPIO_Pin, GPIO_PIN_SET);
    HAL_Delay(100);

    // VBAT 电压寄存器使用这个进行通信验证
    // while (IP2366_GetVbat(ip) < 7000)
    // {
    // }
    // LOGI(TAG, "Vbat: %d", IP2366_GetVbat(ip));

    IP2366_Enable(ip);
}

uint16_t IP2366_GetVbat(ip2366_state_t *ip)
{
    uint8_t buf[2] = {0};

    // voltages
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, BATVADC_DAT0, I2C_MEMADD_SIZE_8BIT, buf, 2, 100);
    ip->voltages[0] = (uint16_t)((buf[1] << 8) | buf[0]);

    return ip->voltages[0];
}

uint8_t IP2366_FaultBitmask(ip2366_state_t *ip, uint8_t state_ctl3)
{
    if ((state_ctl3 & (1 << 5)) || (state_ctl3 & (1 << 4)))
    {
        return 8;
    }
    if (ip->voltages[0] < ip->battery_Vempty)
    {
        return 1;
    }
    if (ip->temperature > 45)
    {
        return 16;
    }
    if (ip->temperature < 0)
    {
        return 32;
    }
    return 0;
}

void IP2366_GetState(ip2366_state_t *ip)
{
    uint8_t buf[2] = {0};
    // current_consumed
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, ISYS_IADC_DAT0, I2C_MEMADD_SIZE_8BIT, buf, 2, 100);
    ip->current_consumed = (uint16_t)((buf[1] << 8) | buf[0]);
    // energy_consumed
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, Vsys_POW_DAT0, I2C_MEMADD_SIZE_8BIT, buf, 2, 100);
    ip->energy_consumed = (uint16_t)((buf[1] << 8) | buf[0]);
    // temperature
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, INTC_IADC_DAT0, I2C_MEMADD_SIZE_8BIT, buf, 1, 100);
    uint8_t intc = buf[0];
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, VGPIO0_NTC_DAT0, I2C_MEMADD_SIZE_8BIT, buf, 2, 100);
    float vntc = ((buf[1] << 8) | buf[0]) * 1e-3;
    if (vntc)
        if (intc)
        {
            ip->temperature = (int16_t)(1 / (fast_log(vntc / 0.8f) / 3380.0f + (1 / (25.0f + 273.15f))) - 273.15f);
        }
        else
        {
            ip->temperature = (int16_t)1 / (fast_log(vntc / 0.2f) / 3380.0f + (1 / (25.0f + 273.15f))) - 273.15f;
        }
    // voltages
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, BATVADC_DAT0, I2C_MEMADD_SIZE_8BIT, buf, 2, 100);
    ip->voltages[0] = (uint16_t)((buf[1] << 8) | buf[0]);
    // battery_remaining
    if (ip->voltages[0])
        ip->battery_remaining = ((ip->voltages[0] - ip->battery_Vempty) * 1.0f / (ip->battery_Vfull - ip->battery_Vempty) * 100.0f);
    // current_battery
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, IBATIADC_DAT0, I2C_MEMADD_SIZE_8BIT, buf, 2, 100);
    ip->current_battery = (int16_t)((buf[1] << 8) | buf[0]);
    // time_remaining
    if (ip->current_battery)
        ip->time_remaining = ip->battery_remaining / 100.0f * ip->battery_capacity / ip->current_battery * 3600;
    // mode
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, STATE_CTL0, I2C_MEMADD_SIZE_8BIT, buf, 1, 100);
    // 第5位为1是charge，3位1是discharge
    ip->mode = (buf[0] & (1 << 5)) ? 1 : (buf[0] & (1 << 3)) ? 2 : (buf[0] & (1 << 4)) ? 3 : 0;
    ip->error_state = (buf[0] & (0 << 3)) ? 2 : (buf[0] & 0b00000111 == 0b00000110) ? 4 : 0;
    // fault_bitmask
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, STATE_CTL3, I2C_MEMADD_SIZE_8BIT, buf, 1, 100);
    ip->fault_bitmask = IP2366_FaultBitmask(ip, buf[0]);
}

void IP2366_Enable(ip2366_state_t *ip)
{
    HAL_GPIO_WritePin(ip->IP_INT_GPIOx, ip->IP_INT_GPIO_Pin, GPIO_PIN_SET);
    // 如果要修改 IP2366 某个寄存器的值， 需要先将对应寄存器的值读出来，
    // 然后对需要修改的 Bit 进行与或运算之后， 把计算得到的值写入该寄存器中
    uint8_t buf[1] = {0};
    // 待机使能和低电电压设置
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, SYS_CTL9, I2C_MEMADD_SIZE_8BIT, buf, 1, 100);
    // 写入待机使能(不使能)
    uint8_t reg_value = (buf[0] & ~0x80) | (0x00 & 0x80);
    HAL_Delay(1);
    HAL_I2C_Mem_Write(ip->ip2366i2c, IP2366_WRITE, SYS_CTL9, I2C_MEMADD_SIZE_8BIT, &reg_value, 1, 100);
}

void IP2366_Sleep(ip2366_state_t *ip)
{
    // P2366 在进入休眠之前， 会切换为输入高阻来
    // 检测 INT 状态， 如果为高电平， 则认为 MCU 不允许 IP2366 进入休眠，
    // 如果为低电平， 则 IP2366 进入休眠；
    // MCU 在检测到 INT 为低后， 16ms 内要停止访问 IC
    HAL_GPIO_WritePin(ip->IP_INT_GPIOx, ip->IP_INT_GPIO_Pin, GPIO_PIN_RESET);

    // 修改(0x09) SYS_CTL9（待机使能和低电电压设置）的第7、6位
    uint8_t buf[1] = {0};
    // 待机使能和低电电压设置
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, SYS_CTL9, I2C_MEMADD_SIZE_8BIT, buf, 1, 100);
    // 写入待机使能(使能)
    uint8_t reg_value = (buf[0] & ~0x80) | (0x80 & 0x80);
    HAL_Delay(1);
    HAL_I2C_Mem_Write(ip->ip2366i2c, IP2366_WRITE, SYS_CTL9, I2C_MEMADD_SIZE_8BIT, &reg_value, 1, 100);
    // 待机使能和低电电压设置
    HAL_Delay(1);
    HAL_I2C_Mem_Read(ip->ip2366i2c, IP2366_READ, SYS_CTL9, I2C_MEMADD_SIZE_8BIT, buf, 1, 100);
    // 非充电情况下立刻进入待机
    reg_value = (buf[0] & ~0xC0) | (0xC0 & 0xC0);
    HAL_Delay(1);
    HAL_I2C_Mem_Write(ip->ip2366i2c, IP2366_WRITE, SYS_CTL9, I2C_MEMADD_SIZE_8BIT, &reg_value, 1, 100);
}

void IP2366_Blink(ip2366_state_t *ip)
{
    static int i = 0;
    static int state = 0;

    if (i % 30 == 0)
    {
        state = state ? 0 : 1;
    }
    if (state)
        IP2366_Sleep(ip);
    else
    {
        IP2366_Enable(ip);
    }

    i++;
}
